package com.syf.controller;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.syf.model.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 完成文件上传的功能
 */
@RestController
@Api(tags = "文件上传的控制器")
public class FileController {


    @Autowired
    private OSS ossClient;  // spring-cloud-alibaba-oss 会自动的注入该对象，报红不要紧

    @Value("${spring.cloud.alicloud.access-key}")
    private String accessId;   //AccessKey ID

    @Value("${oss.bucket.name:syf-imgs}")
    private String bucketName; //Bucket名称

    @Value("${spring.cloud.alicloud.oss.endpoint}")
    private String endPoint; //地域名称

    //1.需要使用内网穿透： http://syf.free.idcfengye.com(公网地址)  -> 127.0.0.1:9000 -> 执行回调函数【203: 上传成功，没回调-显示不了】
    //2.将回调函数jar包上传到服务器: 120.24.169.153:9000
    @Value("${oss.callback.url:120.24.169.153:9000}")
    private String ossCallbackUrl; //上传文件成功后，OSS服务器想回调的callbackUrl(公网) 发一个post请求来告诉后端服务器用户上传文件的情况

    @ApiOperation(value = "上传文件")
    @PostMapping("/image/AliYunImgUpload")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "你要上传的文件")
    })
    public R<String> fileUpload(@RequestParam("file") MultipartFile file) throws IOException { //返回: 能使用浏览器访问到文件路径
        //文件的名称:  2020-10-10 -> 2020/10/10/xxx.jpg
        String fileName = DateUtil.today().replaceAll("-", "/") + "/" + file.getOriginalFilename();
        //上传文件: (bucketName、文件的名称: 日期 +原始的文件名(或者uuid)来做、文件的输入流)
        ossClient.putObject(bucketName, fileName, file.getInputStream());
        //https://coin-exchange-imgs.oss-cn-beijing.aliyuncs.com/2020/10/10/xxx.jpg
        return R.ok("https://" + bucketName + "." + endPoint + "/" + fileName); //能使用浏览器访问到文件路径http://xxx.com/路径
    }


    /**
     *在后端里面不上传图片. 前台访问后端获取一个上传图片的票据,然后前台使用该票据去上传图片.前端上传成功后,
     * OSS 服务器会回调我们后端的服务器,后端服务器响应给 OSS 服务器的内 容,OSS 服务器会把该内容响应给前端
     *  正确的图片URL: https://syf-imgs.oss-cn-shenzhen.aliyuncs.com/2021/08/16/1629103710214.jpg
     *
     *  1.由于回调函数的处理是他自己改动的，其回调地址写的是他的，不是我的
     *      https://coin-exchange-imgs.oss-cn-beijing.aliyuncs.com/2021/08/17/1629172290828.jpg
     *      导致上传图片成功，图片回显地址出错，回显不了
     *
     *
     */
    @GetMapping("/image/pre/upload")
    @ApiOperation(value = "前台 文件的上传获取票据")
    public R<Map<String,String>> preUploadPolicy() {
        String dir = DateUtil.today().replaceAll("-", "/") + "/";
        Map<String, String> policy = getPolicy(30L, 3 * 1024 * 1024L, dir); //获取上传票据: 上传有效时间，文件的大小，时间

        System.out.println("回调完返回 上传图片的票据： "+policy.toString());
        return R.ok(policy) ;
    }

    private Map<String, String> getPolicy(long expireTime, long maxFileSize, String dir) {
        try {
            //1.有效时间
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            //2.PostObject请求最大可支持的文件大小为5 GB，即CONTENT_LENGTH_RANGE为5*1024*1024*1024。
            PolicyConditions policyConds = new PolicyConditions();
            // 设置上传文件的最大的体积
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxFileSize);
            // 设置上传文件上传到那个文件夹(以时间为文件夹name)
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds); // 设置该Policy(票据) 有效时间及上传的大小、上传到哪个文件名

            //不管【整个代码都是copy官网的】
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);

            // 3.返回值
            Map<String, String> respMap = new LinkedHashMap<String, String>();
            respMap.put("accessid", accessId); // 设置我们之前的accessKey（配置文件中）
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", dir); //上传文件夹的路径  eg:2021/04/21/
            respMap.put("host", "https://" + bucketName +"."+ endPoint); // 设置 上传的URL: https://bulkname.endpoint
            respMap.put("expire", String.valueOf(expireEndTime / 1000));
            // respMap.put("expire", formatISO8601Date(expiration));

            JSONObject jasonCallback = new JSONObject();

            //4.OSS服务器要回调的公网ossCallbackUrl，公网再调用我们的服务器
            jasonCallback.put("callbackUrl", ossCallbackUrl); // 当我们的前端把文件上传到oss 服务器成功后
            // oss 服务器会想回调的callbackUrl(公网) 发一个post请求来告诉后端服务器用户上传文件的情况
            jasonCallback.put("callbackBody",
                    "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
            jasonCallback.put("callbackBodyType", "application/x-www-form-urlencoded");
            String base64CallbackBody = BinaryUtil.toBase64String(jasonCallback.toString().getBytes());
            respMap.put("callback", base64CallbackBody);

            return respMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}
